"""
Improvement suggestion models for document analysis system.

Represents actionable improvement recommendations with impact assessment,
effort estimation, and dependency tracking.
"""

import json
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from typing import Dict, List, Optional, Any


class SuggestionType(Enum):
    """Types of improvement suggestions."""
    ADD_CONTENT = "add_content"
    CLARIFY_DESCRIPTION = "clarify_description"
    SPECIFY_METRICS = "specify_metrics"
    ADD_EXAMPLES = "add_examples"
    IMPROVE_STRUCTURE = "improve_structure"
    DEFINE_TERMINOLOGY = "define_terminology"
    ADD_ACCEPTANCE_CRITERIA = "add_acceptance_criteria"
    STANDARDIZE_FORMAT = "standardize_format"


class SuggestionImpact(Enum):
    """Expected impact levels for suggestions."""
    HIGH = "high"
    MEDIUM = "medium"
    LOW = "low"


@dataclass
class ImprovementSuggestion:
    """Represents an actionable improvement recommendation."""

    # Core attributes
    id: str
    analysis_result_id: str
    suggestion_type: SuggestionType
    impact: SuggestionImpact

    # Content
    title: str
    description: str
    specific_action: str
    example: str = ""

    # Context
    target_section: str = ""
    target_text: str = ""
    rationale: str = ""

    # Implementation
    effort_estimate: str = ""  # low/medium/high or hours
    dependencies: List[str] = field(default_factory=list)

    # Additional metadata
    created_at: datetime = field(default_factory=datetime.now)
    tags: List[str] = field(default_factory=list)
    priority_score: float = 0.0
    status: str = "pending"  # pending, accepted, implemented, rejected

    def __post_init__(self):
        """Calculate derived attributes after initialization."""
        self._calculate_priority_score()
        self._validate_status()

    def _calculate_priority_score(self):
        """Calculate priority score based on impact and effort."""
        impact_scores = {
            SuggestionImpact.HIGH: 3.0,
            SuggestionImpact.MEDIUM: 2.0,
            SuggestionImpact.LOW: 1.0
        }

        effort_scores = {
            "low": 1.0,
            "medium": 0.7,
            "high": 0.4,
            "": 0.5  # Default for unknown effort
        }

        # Base score from impact
        impact_score = impact_scores.get(self.impact, 1.0)

        # Adjust based on effort (lower effort = higher priority)
        effort_factor = effort_scores.get(self.effort_estimate.lower(), 0.5)

        # Adjust based on dependencies (fewer dependencies = higher priority)
        dependency_factor = max(0.3, 1.0 - (len(self.dependencies) * 0.2))

        self.priority_score = round(impact_score * effort_factor * dependency_factor, 2)

    def _validate_status(self):
        """Validate status value."""
        valid_statuses = ["pending", "accepted", "implemented", "rejected", "deferred"]
        if self.status not in valid_statuses:
            raise ValueError(f"Invalid status: {self.status}. Must be one of {valid_statuses}")

    def accept(self):
        """Mark suggestion as accepted."""
        self.status = "accepted"

    def implement(self):
        """Mark suggestion as implemented."""
        self.status = "implemented"

    def reject(self, reason: str = ""):
        """Mark suggestion as rejected.

        Args:
            reason: Reason for rejection
        """
        self.status = "rejected"
        if reason:
            self.tags.append(f"rejected: {reason}")

    def defer(self, reason: str = ""):
        """Defer suggestion for later implementation.

        Args:
            reason: Reason for deferring
        """
        self.status = "deferred"
        if reason:
            self.tags.append(f"deferred: {reason}")

    def add_dependency(self, suggestion_id: str):
        """Add dependency to another suggestion.

        Args:
            suggestion_id: ID of dependent suggestion
        """
        if suggestion_id not in self.dependencies:
            self.dependencies.append(suggestion_id)

    def remove_dependency(self, suggestion_id: str):
        """Remove dependency.

        Args:
            suggestion_id: ID of suggestion to remove
        """
        if suggestion_id in self.dependencies:
            self.dependencies.remove(suggestion_id)

    def add_tag(self, tag: str):
        """Add tag to suggestion.

        Args:
            tag: Tag to add
        """
        if tag and tag not in self.tags:
            self.tags.append(tag)

    def remove_tag(self, tag: str):
        """Remove tag from suggestion.

        Args:
            tag: Tag to remove
        """
        if tag in self.tags:
            self.tags.remove(tag)

    def get_impact_value(self) -> int:
        """Get numeric impact value for sorting.

        Returns:
            Numeric impact value (higher = more impact)
        """
        impact_values = {
            SuggestionImpact.HIGH: 3,
            SuggestionImpact.MEDIUM: 2,
            SuggestionImpact.LOW: 1
        }
        return impact_values.get(self.impact, 1)

    def get_type_display_name(self) -> str:
        """Get human-readable type name.

        Returns:
            Display name for suggestion type
        """
        display_names = {
            SuggestionType.ADD_CONTENT: "Add Content",
            SuggestionType.CLARIFY_DESCRIPTION: "Clarify Description",
            SuggestionType.SPECIFY_METRICS: "Specify Metrics",
            SuggestionType.ADD_EXAMPLES: "Add Examples",
            SuggestionType.IMPROVE_STRUCTURE: "Improve Structure",
            SuggestionType.DEFINE_TERMINOLOGY: "Define Terminology",
            SuggestionType.ADD_ACCEPTANCE_CRITERIA: "Add Acceptance Criteria",
            SuggestionType.STANDARDIZE_FORMAT: "Standardize Format"
        }
        return display_names.get(self.suggestion_type, self.suggestion_type.value)

    def get_impact_display_name(self) -> str:
        """Get human-readable impact name.

        Returns:
            Display name for impact
        """
        return self.impact.value.capitalize()

    def is_implemented(self) -> bool:
        """Check if suggestion is implemented.

        Returns:
            True if implemented
        """
        return self.status == "implemented"

    def is_active(self) -> bool:
        """Check if suggestion needs attention.

        Returns:
            True if pending or accepted
        """
        return self.status in ["pending", "accepted"]

    def has_dependencies(self) -> bool:
        """Check if suggestion has dependencies.

        Returns:
            True if has dependencies
        """
        return len(self.dependencies) > 0

    def get_age_days(self) -> int:
        """Get age of suggestion in days.

        Returns:
            Age in days
        """
        delta = datetime.now() - self.created_at
        return delta.days

    def can_be_implemented(self, implemented_suggestions: List[str] = None) -> bool:
        """Check if suggestion can be implemented based on dependencies.

        Args:
            implemented_suggestions: List of implemented suggestion IDs

        Returns:
            True if can be implemented
        """
        if not self.has_dependencies():
            return True

        if implemented_suggestions is None:
            return False

        return all(dep_id in implemented_suggestions for dep_id in self.dependencies)

    def get_effort_hours_estimate(self) -> Optional[float]:
        """Get effort estimate in hours.

        Returns:
            Effort estimate in hours, or None if not specified
        """
        effort_mapping = {
            "low": 2.0,
            "medium": 8.0,
            "high": 24.0
        }

        effort_str = self.effort_estimate.lower()
        if effort_str in effort_mapping:
            return effort_mapping[effort_str]

        # Try to parse hours from string
        try:
            if "hour" in effort_str:
                return float(effort_str.split()[0])
        except (ValueError, IndexError):
            pass

        return None

    def to_dict(self) -> Dict[str, Any]:
        """Convert suggestion to dictionary.

        Returns:
            Dictionary representation
        """
        return {
            "id": self.id,
            "analysis_result_id": self.analysis_result_id,
            "suggestion_type": self.suggestion_type.value,
            "impact": self.impact.value,
            "title": self.title,
            "description": self.description,
            "specific_action": self.specific_action,
            "example": self.example,
            "target_section": self.target_section,
            "target_text": self.target_text,
            "rationale": self.rationale,
            "effort_estimate": self.effort_estimate,
            "dependencies": self.dependencies,
            "created_at": self.created_at.isoformat(),
            "tags": self.tags,
            "priority_score": self.priority_score,
            "status": self.status
        }

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'ImprovementSuggestion':
        """Create suggestion from dictionary.

        Args:
            data: Dictionary data

        Returns:
            ImprovementSuggestion instance
        """
        # Convert date string to datetime object
        if data.get("created_at"):
            data["created_at"] = datetime.fromisoformat(data["created_at"])

        # Convert enum strings to enum objects
        if isinstance(data.get("suggestion_type"), str):
            data["suggestion_type"] = SuggestionType(data["suggestion_type"])
        if isinstance(data.get("impact"), str):
            data["impact"] = SuggestionImpact(data["impact"])

        return cls(**data)

    @classmethod
    def create_high_impact(
        cls,
        id: str,
        analysis_result_id: str,
        suggestion_type: SuggestionType,
        title: str,
        description: str,
        specific_action: str,
        effort_estimate: str = "medium"
    ) -> 'ImprovementSuggestion':
        """Create high impact suggestion.

        Args:
            id: Unique identifier
            analysis_result_id: Parent analysis result ID
            suggestion_type: Type of suggestion
            title: Suggestion title
            description: Detailed description
            specific_action: Specific action to take
            effort_estimate: Effort required

        Returns:
            ImprovementSuggestion instance with high impact
        """
        return cls(
            id=id,
            analysis_result_id=analysis_result_id,
            suggestion_type=suggestion_type,
            impact=SuggestionImpact.HIGH,
            title=title,
            description=description,
            specific_action=specific_action,
            effort_estimate=effort_estimate
        )

    def __str__(self) -> str:
        """String representation."""
        return (f"ImprovementSuggestion(id={self.id}, type={self.suggestion_type.value}, "
                f"impact={self.impact.value}, status={self.status})")

    def __lt__(self, other) -> bool:
        """Less than comparison for sorting by priority."""
        if not isinstance(other, ImprovementSuggestion):
            return NotImplemented

        # Sort by priority score (high first)
        return self.priority_score > other.priority_score


# Utility functions for suggestion management

def filter_suggestions_by_impact(
    suggestions: List[ImprovementSuggestion],
    impact: SuggestionImpact
) -> List[ImprovementSuggestion]:
    """Filter suggestions by impact level.

    Args:
        suggestions: List of improvement suggestions
        impact: Impact to filter by

    Returns:
        Filtered list
    """
    return [s for s in suggestions if s.impact == impact]


def filter_suggestions_by_type(
    suggestions: List[ImprovementSuggestion],
    suggestion_type: SuggestionType
) -> List[ImprovementSuggestion]:
    """Filter suggestions by type.

    Args:
        suggestions: List of improvement suggestions
        suggestion_type: Type to filter by

    Returns:
        Filtered list
    """
    return [s for s in suggestions if s.suggestion_type == suggestion_type]


def get_active_suggestions(suggestions: List[ImprovementSuggestion]) -> List[ImprovementSuggestion]:
    """Get active (pending or accepted) suggestions.

    Args:
        suggestions: List of improvement suggestions

    Returns:
        List of active suggestions
    """
    return [s for s in suggestions if s.is_active()]


def get_implementable_suggestions(
    suggestions: List[ImprovementSuggestion],
    implemented_suggestions: List[str] = None
) -> List[ImprovementSuggestion]:
    """Get suggestions that can be implemented.

    Args:
        suggestions: List of improvement suggestions
        implemented_suggestions: List of implemented suggestion IDs

    Returns:
        List of implementable suggestions
    """
    return [
        s for s in suggestions
        if s.is_active() and s.can_be_implemented(implemented_suggestions or [])
    ]


def sort_suggestions_by_priority(suggestions: List[ImprovementSuggestion]) -> List[ImprovementSuggestion]:
    """Sort suggestions by priority score.

    Args:
        suggestions: List of improvement suggestions

    Returns:
        Sorted list (high priority first)
    """
    return sorted(suggestions)


def get_suggestion_statistics(suggestions: List[ImprovementSuggestion]) -> Dict[str, Any]:
    """Get statistics for improvement suggestions.

    Args:
        suggestions: List of improvement suggestions

    Returns:
        Statistics dictionary
    """
    total = len(suggestions)
    if total == 0:
        return {"total": 0}

    # Count by impact
    impact_counts = {}
    for impact in SuggestionImpact:
        count = len([s for s in suggestions if s.impact == impact])
        impact_counts[impact.value] = count

    # Count by type
    type_counts = {}
    for suggestion_type in SuggestionType:
        count = len([s for s in suggestions if s.suggestion_type == suggestion_type])
        type_counts[suggestion_type.value] = count

    # Count by status
    status_counts = {}
    for status in ["pending", "accepted", "implemented", "rejected", "deferred"]:
        count = len([s for s in suggestions if s.status == status])
        status_counts[status] = count

    # Average priority score
    avg_priority = sum(s.priority_score for s in suggestions) / total

    # Effort estimates
    effort_hours = [s.get_effort_hours_estimate() for s in suggestions if s.get_effort_hours_estimate()]
    total_effort_hours = sum(effort_hours) if effort_hours else 0

    # Age statistics
    ages = [s.get_age_days() for s in suggestions]
    avg_age = sum(ages) / len(ages) if ages else 0

    return {
        "total": total,
        "impact_distribution": impact_counts,
        "type_distribution": type_counts,
        "status_distribution": status_counts,
        "average_priority_score": round(avg_priority, 2),
        "total_effort_hours_estimate": round(total_effort_hours, 1),
        "average_age_days": round(avg_age, 1),
        "active_count": len(get_active_suggestions(suggestions)),
        "implemented_count": len([s for s in suggestions if s.is_implemented()]),
        "implementable_count": len(get_implementable_suggestions(suggestions))
    }